﻿using System;
using System.Data;
using System.Diagnostics;

using Microsoft.SharePoint;

namespace Datagility.Shpt.SPDocMirror.Service
{
    /// <summary>
    /// Provides access to the Audit Log of a SharePoint Site Collection. Auditing must be enabled and
    ///  configured within SharePoint in order for audit entries to be created - see 
    ///  http://msdn.microsoft.com/en-us/library/bb397403(office.12).aspx for more details. Only changes 
    ///  to documents in Document Libraries are handled by this class.
    /// </summary>
    internal class AuditLog: IDisposable
    {
        SPSite siteCollection = null;

        internal struct ItemIds
        {
            internal string ListId;
            internal string WebId;
        }

        public AuditLog(SPSite SiteCollection)
        {
            if (SiteCollection == null)
            {
                throw new ArgumentException("Parameter Cannot be Null", "SiteCollection");
            }

            siteCollection = SiteCollection;
        }

        public AuditLog(string SiteUrl)
        {
            if (SiteUrl == null)
            {
                throw new ArgumentException("Parameter Cannot be Null", "SiteUrl");
            }

            siteCollection = new SPSite(SiteUrl);
        }

        /// <summary>
        /// Builds a <see cref="Microsoft.SharePoint.SPAuditQuery"/> object representing the required
        ///  Audit Log query.
        /// </summary>
        /// <returns>The Audit Log query</returns>
        internal SPAuditQuery BuildDocQuery()
        {
            SPAuditQuery query = new SPAuditQuery(siteCollection);
            query.AddEventRestriction(SPAuditEventType.Delete);
            query.AddEventRestriction(SPAuditEventType.Move);
            query.AddEventRestriction(SPAuditEventType.Undelete);
            query.AddEventRestriction(SPAuditEventType.Update);

            return query;
        }

        /// <summary>
        /// If no startDate is specified then use the SQL Server default
        ///  start date (for no particular reason).
        /// </summary>
        /// <param name="endDate"></param>
        /// <returns></returns>
        public AuditEntryCollection GetChanges(DateTime endDate, string enabledLibraries)
        {
            return GetChanges(new DateTime(1900, 01, 01), endDate, enabledLibraries);
        }

        /// <summary>
        /// Get the Audit Log entries that meet the requirements and that fall between the startDate
        ///  and endDate values.
        /// </summary>
        /// <param name="startDate">The earliest Log entry to return. This must be a UTC dateTime</param>
        /// <param name="endDate">The latest Log entry to return. This must be a UTC dateTime</param>
        /// <returns>A <see cref="Datagility.Shpt.DocMirror.Lib.AuditEntryCollection"/> object</returns>
        public AuditEntryCollection GetChanges(DateTime startDate, DateTime endDate, string enabledLibraries)
        {
            SPMirrorLoggingService.Write("Information", String.Format("AuditLog.GetChanges Starting... - startDate = {0}, endDate = {1}",
                startDate.ToString("yyyy/MM/dd hh:mm:ss"), endDate.ToString("yyyy/MM/dd hh:mm:ss")));

            SPAuditQuery docQuery = BuildDocQuery();

            docQuery.SetRangeStart(startDate);
            docQuery.SetRangeEnd(endDate);

            SPAuditEntryCollection auditCol =
                siteCollection.Audit.GetEntries(docQuery);
            SPMirrorLoggingService.Write("Information", String.Format("AuditLog.GetChanges  - Got {0} Entries", auditCol.Count.ToString()));

            AuditEntryCollection entCol = new AuditEntryCollection();
            if(auditCol.Count > 0)
            {
                //Build AuditEntry objects from the returned Log entries and add them to the collection
                foreach (SPAuditEntry spentry in auditCol)
                {
                    if (spentry.ItemType == SPAuditItemType.Document)
                    {
                        //Only consider items from libraries that have been enabled
                        ItemIds itemIds = GetListIdFromItemId(siteCollection, spentry.ItemId.ToString("B"));
                        if (itemIds.ListId != null && enabledLibraries.Contains(itemIds.ListId))
                        {
                            SPMirrorLoggingService.Write("Information", "AuditLog.GetChanges  - Building Document Entry");

                            AuditEntry entry = new AuditEntry();
                            entry.DocLocation = spentry.DocLocation;
                            switch (spentry.Event)
                            {
                                case SPAuditEventType.Delete:
                                    entry.Event = AuditEventType.Delete;
                                    break;
                                case SPAuditEventType.Move:
                                    entry.Event = AuditEventType.Move;
                                    break;
                                case SPAuditEventType.Undelete:
                                    entry.Event = AuditEventType.Undelete;
                                    break;
                                case SPAuditEventType.Update:
                                    entry.Event = AuditEventType.Update;
                                    break;
                            }
                            entry.EventData = spentry.EventData;
                            entry.ItemId = spentry.ItemId;
                            entry.Occurred = spentry.Occurred;
                            entry.ListId = new Guid(itemIds.ListId);
                            entry.WebId = new Guid(itemIds.WebId);
                            entry.SiteId = spentry.SiteId;
                            entry.UserId = spentry.UserId;

                            SPMirrorLoggingService.Write("Information", String.Format("AuditLog.GetChanges  - Built Document Entry: {0}", entry.ToString()));

                            entCol.Add(entry);
                        }
                    }
                }
            }
            SPMirrorLoggingService.Write("Information", "AuditLog.GetChanges Complete");

            return entCol;
        }

        internal ItemIds GetListIdFromItemId(SPSite siteCollection, string ItemId)
        {
            ItemIds itemIds = new ItemIds();
            DataTable results = null;

            using (SPWeb web = siteCollection.OpenWeb())
            {
                SPSiteDataQuery query = new SPSiteDataQuery();

                // Query all Web sites in this site collection.
                query.Webs = "<Webs Scope=\"SiteCollection\">";

                // Ask for document libraries.
                query.Lists = "<Lists BaseType=\"1\" />";

                // Select the records.
                query.Query = "<Where><Eq>";
                query.Query += "<FieldRef Name=\"UniqueId\" />";
                query.Query += String.Format("<Value Type=\"GUID\">{0}</Value>", ItemId);
                query.Query += "</Eq></Where>";

                // Submit the query.
                results = web.GetSiteData(query);
            }

            if (results.Rows.Count > 0)
            {
                itemIds.ListId = results.Rows[0]["ListId"].ToString();
                itemIds.WebId = results.Rows[0]["WebId"].ToString();
            }

            return itemIds;
        }

        /// <summary>
        /// Deletes all entries from the Audit Log for a Site Collection. Use with caution.
        /// </summary>
        internal void ClearAll()
        {
            DateTime endDate = DateTime.Now.AddMilliseconds(1).ToUniversalTime();
            siteCollection.Audit.DeleteEntries(endDate);
        }

        /// <summary>
        /// 
        /// </summary>
        public void Dispose()
        {
            if (siteCollection != null)
            {
                siteCollection.Dispose();
            }
        }
    }
}
